//
// Created by 郝羽 on 2018/6/14.
//
#include "report.hpp"

#include <map>
#include <list>

#include "metric.hpp"

static void report_fill_time(Report &report) {
    auto sysNow = std::chrono::system_clock::now();
    auto sysNowTimeT = std::chrono::system_clock::to_time_t(sysNow);
    char sysNowStr[100];

    //std::locale::global(std::locale("zh_CN.utf8"));
    strftime(sysNowStr, sizeof(sysNowStr), "%c", localtime(&sysNowTimeT));
    report.configs.emplace_back(KVDesc{"开始时间", std::string(sysNowStr), ""});
}

void report_fill_args(int argc, const char *const *argv, Report &report) {
    std::ostringstream opts;
    for (int i = 0; i < argc; ++i) {
        opts << std::string(argv[i]) << " ";
    }

    report.configs.emplace_back(KVDesc{"参数", opts.str(), ""});
}

void report_fill_concurrency(Report &report, MetricsByTime &metrics) {
    if (metrics.empty()) {
        return;
    }

    DataSet ds_conns{.label = "发起", .color="olive", .points = std::list<Point>{}};
    DataSet ds_conns_setup{.label = "成功", .color="lime", .points = std::list<Point>{}};

    for (auto& it : metrics) {
        auto time = it.first;
        auto metric = it.second;

        auto conns = metric.inited.Get();
        auto conns_setup = metric.setup.Get();

        if (conns) {
            ds_conns.points.emplace_back(Point{time, conns.Get()});
        }
        if (conns_setup) {
            ds_conns_setup.points.emplace_back(Point{time, conns_setup.Get()});
        }
    }

    int x_min = metrics.begin()->first;
    int x_max = metrics.rbegin()->first;

    report.graphs.emplace_back(Graph{"line", "g-conns", "连接数", x_min, x_max, "秒", "连接数", std::list<DataSet>{ds_conns, ds_conns_setup}});
}

void report_fill_bandwidth(Report &report, MetricsByTime &metrics) {
    if (metrics.empty()) {
        return;
    }

    DataSet ds_send{.label = "发带宽", .color="olive", .points = std::list<Point>{}};
    DataSet ds_recv{.label = "收带宽", .color="lime", .points = std::list<Point>{}};

    for (auto& it : metrics) {
        auto time = it.first;
        auto metric = it.second;

        auto send = metric.send.Get();
        auto recv = metric.recv.Get();

        if (send) {
            ds_send.points.emplace_back(Point{time, int(send.Get() / 1000 * 8)});
        }
        if (recv) {
            ds_recv.points.emplace_back(Point{time, int(recv.Get() / 1000 * 8)});
        }
    }

    int x_min = metrics.begin()->first;
    int x_max = metrics.rbegin()->first;

    report.graphs.emplace_back(Graph{"line", "g-bw", "带宽", x_min, x_max, "秒", "kbits", std::list<DataSet>{ds_send, ds_recv}});
}

void report_fill_packets(Report &report, MetricsByTime &metrics) {
    if (metrics.empty()) {
        return;
    }

    DataSet ds_send{.label = "发包数", .color="olive", .points = std::list<Point>{}};
    DataSet ds_recv{.label = "收包数", .color="lime", .points = std::list<Point>{}};

    for (auto& it : metrics) {
        auto time = it.first;
        auto metric = it.second;

        auto send = metric.send_packet.Get();
        auto recv = metric.recv_packet.Get();

        if (send) {
            ds_send.points.emplace_back(Point{time, int(send.Get())});
        }
        if (recv) {
            ds_recv.points.emplace_back(Point{time, int(recv.Get())});
        }
    }

    int x_min = metrics.begin()->first;
    int x_max = metrics.rbegin()->first;

    report.graphs.emplace_back(Graph{"line", "g-pkts", "包数", x_min, x_max, "秒", "个", std::list<DataSet>{ds_send, ds_recv}});
}


void report_fill_rtt(Report &report, MetricsByTime &metrics) {
    if (metrics.empty()) {
        return;
    }

    DataSet ds_rtt_avg{.label = "平均", .color="green", .points = std::list<Point>{}};
    DataSet ds_rtt_min{.label = "最小", .color="olive", .points = std::list<Point>{}};
    DataSet ds_rtt_max{.label = "最大", .color="lime", .points = std::list<Point>{}};

    for (auto& it : metrics) {
        auto time = it.first;
        auto metric = it.second;

        auto rtt_avg = metric.rtt.Avg();
        auto rtt_min = metric.rtt.Min();
        auto rtt_max = metric.rtt.Max();

        if (rtt_avg) {
            ds_rtt_avg.points.emplace_back(Point{time, rtt_avg.Get()});
        }
        if (rtt_min) {
            ds_rtt_min.points.emplace_back(Point{time, rtt_min.Get()});
        }
        if (rtt_max) {
            ds_rtt_max.points.emplace_back(Point{time, rtt_max.Get()});
        }
    }

    int x_min = metrics.begin()->first;
    int x_max = metrics.rbegin()->first;

    report.graphs.emplace_back(Graph{"line", "g-rtt", "环路延迟", x_min, x_max, "秒", "毫秒", std::list<DataSet>{ds_rtt_avg, ds_rtt_min, ds_rtt_max}});
}

void report_fill_jitter(Report &report, MetricsByTime metrics) {
    if (metrics.empty()) {
        return;
    }

    DataSet ds_jitter_avg{.label = "平均", .color="green", .points = std::list<Point>{}};
    DataSet ds_jitter_min{.label = "最小", .color="olive", .points = std::list<Point>{}};
    DataSet ds_jitter_max{.label = "最大", .color="lime", .points = std::list<Point>{}};

    for (auto& it : metrics) {
        auto time = it.first;
        auto metric = it.second;

        auto jitter_avg = metric.jitter.Avg();
        auto jitter_min = metric.jitter.Min();
        auto jitter_max = metric.jitter.Max();

        if (jitter_avg) {
            ds_jitter_avg.points.emplace_back(Point{time, jitter_avg.Get()});
        }
        if (jitter_min) {
            ds_jitter_min.points.emplace_back(Point{time, jitter_min.Get()});
        }
        if (jitter_max) {
            ds_jitter_max.points.emplace_back(Point{time, jitter_max.Get()});
        }
    }

    int x_min = metrics.begin()->first;
    int x_max = metrics.rbegin()->first;

    report.graphs.emplace_back(Graph{"line", "g-jitter", "网络抖动", x_min, x_max,
            "秒", "毫秒", std::list<DataSet>{ds_jitter_avg, ds_jitter_min, ds_jitter_max}});
}

void report_fill_signal_time(Report &report, MetricsByTime &metrics) {
    if (metrics.empty()) {
        return;
    }

    DataSet ds_time_avg{.label = "平均", .color="green", .points = std::list<Point>{}};
    DataSet ds_time_min{.label = "最小", .color="olive", .points = std::list<Point>{}};
    DataSet ds_time_max{.label = "最大", .color="lime", .points = std::list<Point>{}};

    for (auto& it : metrics) {
        auto time = it.first;
        auto metric = it.second;
        auto time_avg = metric.ping_time.Avg();
        auto time_min = metric.ping_time.Min();
        auto time_max = metric.ping_time.Max();

        if (time_avg) {
            ds_time_avg.points.emplace_back(Point{time, time_avg.Get()});
        }
        if (time_min) {
            ds_time_min.points.emplace_back(Point{time, time_min.Get()});
        }
        if (time_max) {
            ds_time_max.points.emplace_back(Point{time, time_max.Get()});
        }
    }

    int x_min = metrics.begin()->first;
    int x_max = metrics.rbegin()->first;

    report.graphs.emplace_back(Graph{"line", "g-signal-time", "信令响应时间", x_min, x_max, "秒", "毫秒", std::list<DataSet>{ds_time_avg, ds_time_min, ds_time_max}});
}

void report_fill_setup_time(Report &report, MetricsByTime &metrics) {
    if (metrics.empty()) {
        return;
    }

    DataSet ds_setup_time_avg{.label = "平均", .color="green", .points = std::list<Point>{}};
    DataSet ds_setup_time_min{.label = "最小", .color="olive", .points = std::list<Point>{}};
    DataSet ds_setup_time_max{.label = "最大", .color="lime", .points = std::list<Point>{}};

    for (auto& it : metrics) {
        auto time = it.first;
        auto metric = it.second;
        auto setup_time_avg = metric.setup_time.Avg();
        auto setup_time_min = metric.setup_time.Min();
        auto setup_time_max = metric.setup_time.Max();

        if (setup_time_avg) {
            ds_setup_time_avg.points.emplace_back(Point{time, setup_time_avg.Get()});
        }
        if (setup_time_min) {
            ds_setup_time_min.points.emplace_back(Point{time, setup_time_min.Get()});
        }
        if (setup_time_max) {
            ds_setup_time_max.points.emplace_back(Point{time, setup_time_max.Get()});
        }
    }

    int x_min = metrics.begin()->first;
    int x_max = metrics.rbegin()->first;

    report.graphs.emplace_back(Graph{"line", "g-conn-speed", "握手时间", x_min, x_max, "秒", "毫秒", std::list<DataSet>{ds_setup_time_avg, ds_setup_time_min, ds_setup_time_max}});
}

void report(int argc, char* argv[], MetricsByTime& metrics) {
    Report report;

    report_fill_time(report);
    report_fill_args(argc, argv, report);

    report_fill_concurrency(report, metrics);
    report_fill_signal_time(report, metrics);
    report_fill_setup_time(report, metrics);
    report_fill_bandwidth(report, metrics);
    report_fill_packets(report, metrics);
    report_fill_rtt(report, metrics);
    report_fill_jitter(report, metrics);

    Printer printer;
    printer.Print(report);
}